package com.doge.calcalbum.business.account

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.doge.calcalbum.App
import com.doge.calcalbum.api.*
import com.doge.calcalbum.api.model.UserInfo
import com.doge.calcalbum.business.vip.AppVipViewModel
import com.doge.calcalbum.security.AESUtils
import com.doge.calcalbum.utils.*
import io.reactivex.disposables.Disposable

class AppUserViewModel : ViewModel() {
    private val security = "392D1AC8E63A8B62CF3FC264D1050431545BF22D"
    private val folder = "udR2h1k1a6l3sXe5d4r"
    private val key get() = "wiklir3i"
    val user = MutableLiveData<UserInfo?>()
    private var retryTask: Disposable? = null

    fun self(): UserInfo {
        // any thread
        val cache = user.value
        if (cache != null) return cache
        val diskCache = spReadString(key, null, folder)

        log("appUser self sp diskCache $diskCache")
        if (diskCache?.isNotEmpty() == true) {
            val decryptedUser = AESUtils.decrypt(security, diskCache)
            log("appUser self sp diskCache decrypted $decryptedUser")
            fromJson(decryptedUser, UserInfo::class.java)?.let {
                runOnUI { user.value = (it) }
                return it
            }
        }
        val none = UserInfo.EMPTY
        runOnUI { user.value = none }
        return none
    }

    fun updateSelf(data: UserInfo) {
        require(isMainThread())
        user.value = data
        log("appUser self updateSelf $data")
        AppVipViewModel.get().refreshLimitCount()

        runOnIO {
            val user = toJson(data)
            val encryptedUser = AESUtils.encrypt(security, user)
            log("appUser self sp put $user")
            log("appUser self sp put encrypted $encryptedUser")
            spWrite(key, encryptedUser, folder)
        }
    }

    fun requestSelfInfo(retry: Int = 0) {
        log("appUser self requestSelfInfo")
        retryTask?.dispose()
        api.userInfo()
            .map(ApiResultMapTransformer())
            .subscribeOnIO()
            .observeOnMain()
            .subscribe(object : ObserverAdapter<UserInfo>() {
                override fun onNext(t: UserInfo) {
                    spWrite(failureTimestamp, null, folder)
                    updateSelf(t)
                }

                override fun onError(e: Throwable) {
                    if (retry < 5) {
                        retryTask?.dispose()
                        retryTask = rxDelay(20000) {
                            requestSelfInfo(retry + 1)
                        }
                    } else {
                        val limit: Long = 90 * 24 * 3600 * 1000L
                        val last = spReadLong(failureTimestamp, 0L, folder)
                        if (last == 0L) {
                            spWrite(failureTimestamp, System.currentTimeMillis(), folder)
                        } else if (System.currentTimeMillis() - last > (limit)) {
                            if (e !is ServerException) {
                                toast("请校对网络时间后重启app")
                                Activities.finishAll()
                            }
                        }
                    }
                }
            })
    }

    private val failureTimestamp = "djdqlwrheakd" // fail time

    companion object {
        fun get(): AppUserViewModel {
            return (appContext as App).getAppViewModelProvider().get(AppUserViewModel::class.java)
        }
    }
}